// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include <stdexcept>

#if defined(PLATFORM_WINDOWS)
#include <windows.h>
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
#include <dlfcn.h>
#endif

#include "Platform/DynamicLibrary.h"

namespace Platform {

class DynamicLibraryImpl {
public:
    explicit DynamicLibraryImpl(const Path& path)
    {
#if defined(PLATFORM_WINDOWS)
        handle = LoadLibraryA(path.string().c_str());
        if (!handle) {
            throw DynamicLibrary::OpenFailed(path, std::to_string(GetLastError()));
        }
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
        handle = dlopen(path.string().c_str(), RTLD_NOW);
        if (!handle) {
            throw DynamicLibrary::OpenFailed(path, dlerror());
        }
#endif
    }

    ~DynamicLibraryImpl()
    {
        if (handle) {
#if defined(PLATFORM_WINDOWS)
            FreeLibrary(static_cast<HMODULE>(handle));
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
            dlclose(handle);
#endif
        }
    }

    void *GetSymbolRaw(const std::string& name) const
    {
#if defined(PLATFORM_WINDOWS)
        return reinterpret_cast<void *>(GetProcAddress(static_cast<HMODULE>(handle), name.c_str()));
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_MACOS)
        return dlsym(handle, name.c_str());
#endif
    }

private:
#if defined(PLATFORM_WINDOWS)
    HMODULE handle;
#else
    void *handle;
#endif
};

DynamicLibrary::DynamicLibrary(const Path &path)
    : impl(std::make_unique<DynamicLibraryImpl>(path))
{}

DynamicLibrary::~DynamicLibrary() = default;

void *DynamicLibrary::GetSymbolRaw(const std::string& name) const
{
    return impl->GetSymbolRaw(name);
}

DynamicLibrary::OpenFailed::OpenFailed(Path file, std::string errorMessage)
    : File(std::move(file)), ErrorMessage(std::move(errorMessage))
{}

std::string DynamicLibrary::OpenFailed::ToString() const
{
    return "Failed to open " + File.string() + ": " + ErrorMessage;
}

} // namespace Platform
